# coding=utf-8

import os
from typing import Dict, List, Tuple
import warnings
from xml.etree.ElementTree import Element
from xml.etree import ElementTree
import openpyxl
from lxml import etree
from utils.payload import EncodeType, TypeEnum, VhalType
from utils import xml
from utils.util import removeDir, writeFile
from utils.Common import BYTES, FLOAT, INT32


class Signal:

    def __init__(self, name: str, type: TypeEnum, min: str, max: str, factor: str, offset: str, unit: str, textTable: EncodeType, desc: str, used: int = 0, index: int = 0) -> None:
        self.index = index
        self.name = name
        self.used = used
        self.type = type
        self.min = min
        self.max = max
        if factor != "":
            self.factor = factor
        else:
            self.factor = "1"
        if offset != "":
            self.offset = offset
        else:
            self.offset = "0"
        if unit is not None:
            self.unit = str(unit)
        else:
            self.unit = ""
        self.textTable = textTable

        if desc is not None:
            self.desc = desc.replace('\n', '').replace('\r', '')
        else:
            self.desc = ""

    def isFloat(self) -> bool:
        return isinstance(eval(self.factor), float) or isinstance(eval(self.offset), float)

    def getVhalType(self) -> VhalType:
        if self.isFloat():
            return VhalType.float_t
        else:
            return VhalType.int32_t

    def getSignalIdTypeDefine(self) -> Tuple[str, int]:
        if self.isFloat():
            return ("FLOAT", FLOAT)
        else:
            return ("INT32", INT32)

    def toPropType(self):
        (name, _) = self.getSignalIdTypeDefine()
        return name

    def hasEnum(self):
        return self.textTable is not None

    def getEncodeType(self) -> EncodeType:
        return self.textTable

    def getSignalMethodReturnType(self):
        if self.isFloat():
            return "float"
        else:
            if self.hasEnum():
                return "sdb::{}".format(self.textTable.getTypeName())
            else:
                return "int32_t"

    # 获取值定义的类型
    def getValueType(self) -> TypeEnum:
        return self.type

    # 获取信号作为变量时候的名称
    def getVariableName(self, isTX=False, isSignal=False) -> str:
        if not isTX:
            return self.name[:1].lower()+self.name[1:]
        else:
            if isSignal:
                return "value"
            else:
                return self.name[:1].lower()+self.name[1:]

    def __getNtohx(self, member) -> str:
        if self.type == TypeEnum.uint16_t:
            return "ntohs({0})".format(member)
        elif self.type == TypeEnum.uint32_t:
            return "(int32_t)ntohl({0})".format(member)
        else:
            return member

    def __getHtonx(self) -> str:
        if self.type == TypeEnum.uint16_t:
            return "htons"
        elif self.type == TypeEnum.uint32_t:
            return "htonl"
        else:
            return ""

    def readSignalFromNet(self, member: str) -> str:
        fromNet = self.__getNtohx(member)
        factor_unit = "f" if isinstance(eval(self.factor), float) else ""
        offset_unit = "f" if isinstance(eval(self.offset), float) else ""
        if self.factor != "1" and self.offset != "0":
            return "{0}*({1}{2})+({3}{4})".format(fromNet, self.factor, factor_unit, self.offset, offset_unit)
        elif self.factor != "1":
            return "{0}*({1}{2})".format(fromNet, self.factor, factor_unit)
        elif self.offset != "0":
            return "{0}+({1}{2})".format(fromNet, self.offset, offset_unit)
        else:
            return fromNet

    # htonl((uint8_t)signal.value.int32Value[0])
    # htons(signal.value.int32Value[0])
    def setSignalToNet(self, member: str) -> str:
        toNet = self.__getHtonx()
        if self.factor != "1" and self.offset != "0":
            if toNet != "":
                return "{0}(({1})(({2}-{3})/{4}))".format(toNet, self.type.name, member, "({0})".format(self.offset) if str(self.offset).startswith("-") else self.offset, self.factor)
            else:
                return "({0})(({1}-{2})/{3})".format(self.type.name, member, "({0})".format(self.offset) if str(self.offset).startswith("-") else self.offset, self.factor)
        elif self.factor != "1":
            if toNet != "":
                return "{0}(({1})({2}/{3}))".format(toNet, self.type.name, member, self.factor)
            else:
                return "({0})({1}/{2})".format(self.type.name, member, self.factor)
        elif self.offset != "0":
            if toNet != "":
                return "{0}(({1})({2}-{3}))".format(toNet, self.type.name, member, "({0})".format(self.offset) if str(self.offset).startswith("-") else self.offset)
            else:
                return "({0})({1}-{2})".format(self.type.name, member, "({0})".format(self.offset) if str(self.offset).startswith("-") else self.offset)
        else:
            if toNet != "":
                return "{0}(({1}){2})".format(toNet, self.type.name, member)
            else:
                return "({0}){1}".format(self.type.name, member)

    def __eq__(self, __o: object) -> bool:
        return __o.name == self.name


class TXGroup:

    def __init__(self, name, op_code: int = 0, used: int = 0) -> None:
        self.name = name
        self.opcode = op_code
        self.used = used
        self.signals: List[Signal] = []

    def addSignal(self, signal: Signal):
        if signal in self.signals:
            return
        self.signals.append(signal)

    # 检查是否是单个设置信号
    def isSingle(self):
        return len(self.signals) == 1

    def toProtoBufContent(self):
        contents = []
        index = 0
        isSignal = self.isSingle()
        if not isSignal:
            for signal in self.signals:
                type = signal.type
                name = signal.getVariableName(True, isSignal)
                if type.name in ["int8_t", "uint8_t", "int16_t", "uint16_t", "int32_t", "uint32_t"]:
                    index += 1
                    contents.append(
                        "required int32 {} = {};".format(name, index))
                elif type.name in ["int64_t", "uint64_t"]:
                    index += 1
                    contents.append(
                        "required int64 {} = {};".format(name, index))
                else:
                    raise Exception("未知类型{}".format(type.name))
        return contents

    def getVhalType(self) -> VhalType:
        if self.isSingle():
            signal = self.signals[0]
            if signal.isFloat():
                return VhalType.float_t
            else:
                return VhalType.int32_t
        else:
            return VhalType.bytes

    def getSignalIdTypeDefine(self) -> Tuple[str, int]:
        if self.isSingle():
            signal = self.signals[0]
            if signal.isFloat():
                return ("FLOAT", FLOAT)
            else:
                return ("INT32", INT32)
        else:
            return ("BYTES", BYTES)

    def toPropType(self):
        (name, _) = self.getSignalIdTypeDefine()
        return name

    def getSignalMethodParamType(self) -> str:
        if self.isSingle():
            signal = self.signals[0]
            if signal.isFloat():
                return "float"
            else:
                if signal.hasEnum():
                    return "sdb::"+signal.getEncodeType().getTypeName()
                else:
                    return "int32_t"
        else:
            return "{}&".format(self.name)

    def toSignalObjMember(self) -> str:
        (_, value) = self.getSignalIdTypeDefine()
        if value == FLOAT:
            return "floatValues"
        elif value == INT32:
            return "int32Values"
        else:
            return "bytes"

    def __eq__(self, __o: object) -> bool:
        return __o.name == self.name


class RXSignalPackage:

    def __init__(self, name, op_code: int = 0) -> None:
        self.name = name
        self.opcode = op_code
        self.signals: List[Signal] = []

    def addSignal(self, signal: Signal):
        if signal in self.signals:
            return
        self.signals.append(signal)


class ECU:

    def __init__(self, name: str, serviceId: int = 0) -> None:
        self.name = name
        self.serviceId = serviceId
        self.rxPackage: RXSignalPackage = RXSignalPackage(
            "Op{}_Notification".format(self.name), 200 if name == "CARCONFIG" or name == "CARCONFIGTHOUSAND" or name == "TIMEOUT" else 999)
        self.txSignals: List[TXGroup] = []

    def hasWriteSignals(self) -> bool:
        return len(self.txSignals) > 0

    def hasReadSignals(self) -> bool:
        return len(self.rxPackage.signals) > 0

    def getServiceId(self) -> Tuple[str, str]:
        return ("serviceID_"+self.name, str(self.serviceId))

    def addTxSignal(self, groupName: str, signal: Signal, opcode=0, used=0):
        signalGroup: TXGroup = None
        for group in self.txSignals:
            if group.name == groupName:
                signalGroup = group
                break
        if signalGroup is None:
            signalGroup = TXGroup(groupName, opcode, used)
            self.txSignals.append(signalGroup)
        signalGroup.addSignal(signal)

    def addRxSignal(self, signal: Signal):
        self.rxPackage.addSignal(signal)

    def sort(self):
        self.txSignals = sorted(
            self.txSignals, key=lambda group: group.opcode)
        self.txSignals = sorted(
            self.txSignals, key=lambda group: group.used == 0)

        # 检查所有index =-1的rx信号重新生存序列
        index = 1
        for signal in self.rxPackage.signals:
            if signal.index == -1:
                signal.index = index
                index += 1
        self.rxPackage.signals = sorted(
            self.rxPackage.signals, key=lambda signal: signal.index)
        self.rxPackage.signals = sorted(
            self.rxPackage.signals, key=lambda signal: signal.used == 0)


class SDB:

    def __init__(self) -> None:
        self.ecus: List[ECU] = []
        self.textTables: Dict[str, EncodeType] = {}

    def addECU(self, ecu: ECU):
        self.ecus.append(ecu)


def toTypeEnum(len: str, baseType: str) -> TypeEnum:
    
    if isinstance(eval(len), float):
        size = int(float(len))
    else:
        size = int(len, 10)
    if size >= 1 and size <= 8:
        if baseType.lower().startswith("unsigned") or baseType.lower().startswith("bool") or baseType.lower().startswith("byte"):
            return TypeEnum.uint8_t
        else:
            return TypeEnum.int8_t
    elif size > 8 and size <= 16:
        if baseType.lower().startswith("signed"):
            return TypeEnum.int16_t
        else:
            return TypeEnum.uint16_t
    elif size > 16 and size <= 32:
        return TypeEnum.uint32_t if baseType.lower() == "unsigned" else TypeEnum.int32_t
    elif size > 32 and size <= 64:
        return TypeEnum.uint64_t if baseType.lower() == "unsigned" else TypeEnum.int64_t
    else:
        raise Exception("未支持的len长度 {}".format(str(size)))


class SDBParser:

    def __init__(self) -> None:
        self.sdb: SDB = SDB()
        self.__sdb_dir="config/ipcp/sdb"
        if not os.path.exists(self.__sdb_dir):
            raise Exception("{} not exist!".format(self.__sdb_dir))
        self.__sdb_file=os.path.join(self.__sdb_dir,"SDB.xlsx")
        self.__ecuDir=os.path.join(self.__sdb_dir,"ecus")
        self.__textTableXml=os.path.join(self.__ecuDir,"TextTables.xml")

    def __loadExcle(self, path) -> openpyxl.Workbook:
        with warnings.catch_warnings(record=True):
            excle = openpyxl.load_workbook(path)
            return excle

    def parse(self):
        self.__readNewXML()

    def upgradeSDB(self, legacy=False):
        excle = self.__loadExcle(self.__sdb_file)
        self.__parseTextTable(excle["Texttables"])
        self.__parseSignalReport(excle["SignalReport"], legacy)
        self.__toXML()

    def __readOneECUXML(self, xml, needUsdIs0=False) -> ECU:
        tree: Element = ElementTree.parse(xml)
        ecuNode = tree.getroot()
        ecu = ECU(ecuNode.get("name", ""), int(ecuNode.get("serviceId", 0)))
        rxSignalsNode = ecuNode.find("RXSignals")
        for signalNode in rxSignalsNode.findall("Signal"):
            used = signalNode.get("used", "0")
            if not needUsdIs0 and int(used) == 0:
                continue
            index = signalNode.get("index", "0")
            nameNode = signalNode.find("name")
            typeNode = signalNode.find("type")
            minNode = signalNode.find("min")
            maxNode = signalNode.find("max")
            factorNode = signalNode.find("factor")
            offsetNode = signalNode.find("offset")
            unitNode = signalNode.find("unit")
            textTableNode = signalNode.find("textTable")
            descNode = signalNode.find("description")
            signal = Signal(nameNode.text, TypeEnum.toEnum(typeNode.text), minNode.text, maxNode.text, factorNode.text, offsetNode.text, unitNode.text,
                            self.sdb.textTables.get(textTableNode.text, None), descNode.text, int(used))
            signal.index = int(index)
            ecu.rxPackage.addSignal(signal)
        txSignalsNodes = ecuNode.find("TXSignals")
        for groupNodes in txSignalsNodes.findall("TXGroup"):
            used = groupNodes.get("used", "0")
            if not needUsdIs0 and int(used) == 0:
                continue
            name = groupNodes.get("name", "")
            opcode = groupNodes.get("opcode", "0")
            for signalNode in groupNodes.findall("Signal"):
                nameNode = signalNode.find("name")
                typeNode = signalNode.find("type")
                minNode = signalNode.find("min")
                maxNode = signalNode.find("max")
                factorNode = signalNode.find("factor")
                offsetNode = signalNode.find("offset")
                unitNode = signalNode.find("unit")
                textTableNode = signalNode.find("textTable")
                descNode = signalNode.find("description")
                signal = Signal(nameNode.text, TypeEnum.toEnum(typeNode.text), minNode.text, maxNode.text, factorNode.text, offsetNode.text, unitNode.text,
                                self.sdb.textTables.get(textTableNode.text, None), descNode.text, int(used))
                ecu.addTxSignal(name, signal, int(opcode), int(used))
        return ecu

    def __readNewXML(self):
        # 载入textTable
        tree: Element = ElementTree.parse(self.__textTableXml)
        textTableNode = tree.getroot()
        for enumNode in textTableNode.findall("Enum"):
            enumName = enumNode.get("name", "")
            if enumName == "":
                continue
            textTable = EncodeType(enumName, isEnum=True)
            for itemNode in enumNode.findall("item"):
                textTable.addMember(itemNode.text, "")
            self.sdb.textTables[enumName] = textTable

        # 载入ecu
        for (root, _, files) in os.walk(self.__ecuDir):
            print("zpy load __ecuDir files:" , files)
            for file in [file for file in sorted(files) if not file.startswith(".") and not file.startswith("TextTables") and file.endswith(".xml")]:
                ecu=self.__readOneECUXML(os.path.join(root, file))
                print("zpy load ecu file:" , file)
                if ecu.hasReadSignals() or ecu.hasWriteSignals():
                    print("zpy load ecu:" , ecu.name)
                    self.sdb.addECU(ecu)

    def __readLasteXML(self) -> Dict[str, ECU]:
        ecuDict = {}
        for (root, _, files) in os.walk(self.__ecuDir):
            for file in [file for file in sorted(files) if not file.startswith(".") and not file.startswith("TextTables") and file.endswith(".xml")]:
                ecu = self.__readOneECUXML(os.path.join(root, file),False)
                if ecu.hasReadSignals() or ecu.hasWriteSignals():
                    ecuDict[ecu.name] = ecu
        return ecuDict
    #包含所有的last xml item，usd=1 和usd=0
    def __readLasteXMLAll(self) -> Dict[str, ECU]:
        ecuDict = {}
        for (root, _, files) in os.walk(self.__ecuDir):
            for file in [file for file in sorted(files) if not file.startswith(".") and not file.startswith("TextTables") and file.endswith(".xml")]:
                ecu = self.__readOneECUXML(os.path.join(root, file),True)
                if ecu.hasReadSignals() or ecu.hasWriteSignals():
                    ecuDict[ecu.name] = ecu
        return ecuDict

    def __readLegacyRXTXXML(self) -> Dict[str, ECU]:
        xmlTXDict = xml.parseXML(os.path.join(self.__sdb_dir,"legacy/TX.xml"))
        xmlRXDict = xml.parseXML(os.path.join(self.__sdb_dir,"legacy/RX.xml"))
        signalList = xmlTXDict["XMASignalDatas"]["SignalItem"]
        signalList.extend(xmlRXDict["XMASignalDatas"]["SignalItem"])
        ecuDict = {}
        # 提取每个ecu对应的信号数据
        for signalItemDict in [signalItem for signalItem in signalList if int(signalItem.get("UB", "1")) == 1 and not str(signalItem["Signal"]).endswith("SignalEnd")]:
            ecuName = signalItemDict["Subscriber"] if signalItemDict["Publisher"] == "IHU" else signalItemDict["Publisher"]
            ecu = ecuDict.get(ecuName, None)
            if ecu is None:
                ecu = ECU(ecuName, int(signalItemDict.get("ServiceID", "0")))
                ecuDict[ecuName] = ecu

            def getSignalName(signalItem) -> str:
                group = signalItem.get("SignalGroup", None)
                return group if group is not None else signalItem["Signal"]
            signal = Signal(signalItemDict["Signal"], "",
                            "", "", "", "", "", None, "", 1, -1)
            if signalItemDict["Publisher"] == "IHU":
                ecu.addTxSignal(getSignalName(signalItemDict), signal, int(signalItemDict["Opcod"]), 1)
            else:
                ecu.addRxSignal(signal)

        return ecuDict

    def __parseSignalReport(self, sheet,legacy=False):
        allECU: Dict[str, ECU] = {}
        lastTxECU=None
        lastRXECU=None
        for row_index in range(2, sheet.max_row+1):
            txECU = sheet.cell(row_index, 1).value
            rxECU = sheet.cell(row_index, 4).value
            sig = sheet.cell(row_index, 13).value
            if txECU is None:
                txECU = lastTxECU
            else:
                lastTxECU = txECU
            if rxECU is None:
                rxECU = lastRXECU
            else:
                lastRXECU = rxECU
            if txECU is None or rxECU is None:
                continue
            # lastSignal = sig
            if rxECU.upper() == "IHU" or txECU.upper() == "IHU":
                ecuName = rxECU if rxECU.upper() != "IHU" else txECU
                len = str(sheet.cell(row_index, 14).value).strip()
                baseType = sheet.cell(row_index, 18).value.strip()
                min = sheet.cell(row_index, 19).value
                if min is None:
                    min = ""
                max = sheet.cell(row_index, 20).value
                if max is None:
                    max = ""
                factor = sheet.cell(row_index, 21).value
                if factor is None:
                    factor = "1"
                offset = sheet.cell(row_index, 22).value
                if offset is None:
                    offset = "0"
                unit = sheet.cell(row_index, 23).value
                if unit is None:
                    unit = ""
                textTable = sheet.cell(row_index, 24).value
                if textTable is None:
                    textTable = ""
                description = str(sheet.cell(row_index, 25).value).strip()
                signal = Signal(sig, toTypeEnum(
                    len, baseType), str(min), str(max), str(factor), str(offset), str(unit), self.sdb.textTables.get(textTable, None), description)
                ecu = allECU.get(ecuName, None)
                if ecu is None:
                    ecu = ECU(ecuName)
                    allECU[ecuName] = ecu
                if rxECU.upper() == "IHU":
                    # read signal
                    ecu.addRxSignal(signal)
                else:
                    # write signal
                    sigGrp = sheet.cell(row_index, 12).value
                    if sigGrp is None or sigGrp.strip() == "":
                        sigGrp = sig
                    ecu.addTxSignal(sigGrp, signal)
        # 对每个ecu进行和旧版本比较
        ecuDictAll = {}

        if legacy:
            ecuDict = self.__readLegacyRXTXXML()
            ecuDictAll = self.__readLegacyRXTXXML()
        else:
            ecuDict = self.__readLasteXML()
            ecuDictAll = self.__readLasteXMLAll()
        for _, ecu in allECU.items():
            # 将已经分配opcode的信号都加入
            oldECU = ecuDictAll.get(ecu.name, None)
            if oldECU is not None:
                self.__compilerECU(ecu, oldECU)
            ecu.sort()
            self.sdb.addECU(ecu)

    def __compilerECU(self, newECU: ECU, oldECU: ECU):
        newECU.serviceId = oldECU.serviceId
        for signal in newECU.rxPackage.signals:
            for oldSignal in oldECU.rxPackage.signals:
                if signal.name == oldSignal.name:
                    signal.index = oldSignal.index
                    signal.used = oldSignal.used
                    break
        for group in newECU.txSignals:
            for oldGroup in oldECU.txSignals:
                if group.name == oldGroup.name:
                    group.opcode = oldGroup.opcode
                    group.used = oldGroup.used
                    break

    def __parseTextTable(self, textTablesSheet):
        textTableDict: Dict[str, List[str]] = {}
        for row_index in range(2, textTablesSheet.max_row+1):
            textTableName = str(textTablesSheet.cell(row_index, 1).value)
            value = str(textTablesSheet.cell(row_index, 2).value)
            if textTableName is None:continue
            if textTableName in textTableDict:
                textTableDict[textTableName].append(value)
            else:
                textTableDict[textTableName] = [value]
        for (enum_name, values) in textTableDict.items():
            members = {}
            textTable = EncodeType(enum_name, isEnum=True)
            for valueIdentify in values:
                (value, variable) = valueIdentify.split("=")
                variable = variable.strip()
                if variable.startswith(enum_name+"_"):
                    variable = variable[len(enum_name+"_"):]
                if variable[0].isdigit():
                    variable = "_"+variable
                if variable in members:
                    members[variable] = members[variable]+1
                    variable = "{0}_{1}".format(variable, members[variable])
                else:
                    members[variable] = 1
                textTable.addMember("{name} = {value}".format(
                    name=variable[0].upper()+variable[1:], value=value))
            self.sdb.textTables[enum_name] = textTable

    def __toXML(self):
        # 先删除ecu目录下的所有内容
        removeDir(self.__ecuDir)
        for ecu in self.sdb.ecus:
            wordbook = etree.Element('ECU')
            wordbook.set("name", ecu.name)
            wordbook.set("serviceId", str(ecu.serviceId))
            rxNode = etree.Element('RXSignals')
            rxNode.set("opcode", str(ecu.rxPackage.opcode))
            wordbook.append(rxNode)
            #遍历出已经占用的id值
            existEcuIds = []
            for group in ecu.txSignals:
                if group.opcode != 0:
                   existEcuIds.append(group.opcode)
            itemOpCode = 1
            for signal in ecu.rxPackage.signals:
                rxNode.append(self.__toSignalXML(signal))

            txNode = etree.Element('TXSignals')
            wordbook.append(txNode)
            for group in ecu.txSignals:
                groupNode = etree.Element("TXGroup")
                groupNode.set("name", group.name)
                if group.opcode == 0:
                    while itemOpCode in existEcuIds:
                        itemOpCode= itemOpCode + 1
                    group.opcode = itemOpCode
                    existEcuIds.append(itemOpCode)
                groupNode.set("opcode", str(group.opcode))
                groupNode.set("used", str(group.used))
                for signal in group.signals:
                    groupNode.append(self.__toSignalXML(signal, False))
                txNode.append(groupNode)

            s = etree.tostring(wordbook, pretty_print=True, encoding='UTF-8')
            writeFile(self.__ecuDir, ecu.name+".xml", lambda file:
                      file.write(str(s, "UTF-8")))
        textTablebook = etree.Element('TextTables')
        for textTable in sorted(self.sdb.textTables.values(), key=lambda texttable: texttable.getTypeName()):
            enumNode = etree.Element("Enum")
            enumNode.set("name", textTable.getTypeName())
            for (member, _) in textTable.members:
                itemNode = etree.Element("item")
                itemNode.text = member
                enumNode.append(itemNode)
            textTablebook.append(enumNode)

        textTableStr = etree.tostring(
            textTablebook, pretty_print=True, encoding='UTF-8')
        writeFile(self.__ecuDir, "TextTables.xml", lambda file:
                  file.write(str(textTableStr, "UTF-8")))

    def __toSignalXML(self, signal: Signal, isRX=True):
        sigNode = etree.Element("Signal")
        if isRX:
            sigNode.set("index", str(signal.index))
            sigNode.set("used", str(signal.used))
        nameNode = etree.Element("name")
        typeNode = etree.Element("type")
        minNode = etree.Element("min")
        maxNode = etree.Element("max")
        factorNode = etree.Element("factor")
        offsetNode = etree.Element("offset")
        unitNode = etree.Element("unit")
        textTableNode = etree.Element("textTable")
        descNode = etree.Element("description")
        nameNode.text = signal.name
        typeNode.text = signal.type.name
        minNode.text = signal.min
        maxNode.text = signal.max
        factorNode.text = signal.factor
        offsetNode.text = signal.offset
        unitNode.text = signal.unit
        if signal.hasEnum():
            textTableNode.text = signal.textTable.getTypeName()
        else:
            textTableNode.text = ""
        descNode.text = signal.desc
        sigNode.append(nameNode)
        sigNode.append(typeNode)
        sigNode.append(minNode)
        sigNode.append(maxNode)
        sigNode.append(factorNode)
        sigNode.append(offsetNode)
        sigNode.append(unitNode)
        sigNode.append(textTableNode)
        sigNode.append(descNode)
        return sigNode